import { isPlainObject } from "lodash-es"

const toJSON = (text: string) => {
  try {
    return JSON.parse(text);
  } catch (error) {
    return text;
  }
}

const getBlob = (url:string) => {
  return new Promise(resolve => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'blob';
    xhr.onload = () => {
      if (xhr.status === 200) {
        resolve(xhr.response);
      }
    };
    xhr.send();
  });
}

const getResult = async (resp:Response,type = "json") => {
  const response = resp.clone();
  let res:any;
  try {
    switch(type){
      case 'text':res = await resp.text();break;
      case 'json':res = await resp.json();break;
      case 'formData':res = await resp.formData();break;
      case 'blob':res = await resp.blob();break;
      case 'arrayBuffer':res = await resp.arrayBuffer();break;
    }
  } catch (error) {
    console.error(error)
    res = await response.text()
  }
  return res;
}

const API = {
  post: (url: string, data: any = {}, options: any = { headers: { "Content-Type": "application/json" } }) => {
    const { headers } = options;
    //
    if (headers?.['Content-Type'] == 'application/x-www-form-urlencoded') {
      delete headers['Content-Type'];
    }
    return new Promise<any>((resolve, reject) => {
      fetch(url, { method: "POST", headers, body: isPlainObject(data) ? JSON.stringify(data) : data })
        .then(async (response) => {
          const ok = response.ok;
          const res = await getResult(response,options.type)
          if (!ok) {
            // ElMessage.warning(res)
            reject(res)
          }
          else {
            resolve(res)
          }
        })
        .catch(e => reject(e))
    })
  },
  get: (url: string, options: any = {}) => {
    const { headers } = options
    if (headers?.['Content-Type'] == 'application/x-www-form-urlencoded') {
      delete headers['Content-Type'];
    }
    return new Promise<any>((resolve, reject) => {
      fetch(url)
        .then(async (response) => {
          const ok = response.ok;
          const res = await getResult(response,options.type)
          if (!ok) {
            // ElMessage.warning(res)
            reject(res)
          }
          else {
            resolve(res)
          }
        })
        .catch(e => reject(e))
    })
  },
  put: (url: string, data: any = {}, options: any = { headers: { "Content-Type": "application/json" } }) => {
    const { headers } = options;
    if (headers?.['Content-Type'] == 'application/x-www-form-urlencoded') {
      delete headers['Content-Type'];
    }
    return new Promise<any>((resolve, reject) => {
      fetch(url, { method: "PUT", headers, body: JSON.stringify(data) })
        .then(async (response) => {
          const ok = response.ok;
          const res = await getResult(response,options.type)
          if (!ok) {
            // ElMessage.warning(res)
            reject(res)
          }
          else {
            resolve(res)
          }
        })
        .catch(e => reject(e))
    })
  },
  delete: (url: string, options: any = {}) => {
    const { headers } = options
    if (headers?.['Content-Type'] == 'application/x-www-form-urlencoded') {
      delete headers['Content-Type'];
    }
    return new Promise<any>((resolve, reject) => {
      fetch(url, { method: "DELETE" })
        .then(async (response) => {
          const ok = response.ok;
          const res = await getResult(response,options.type)
          if (!ok) {
            // ElMessage.warning(res)
            reject(res)
          }
          else {
            resolve(res)
          }
        })
        .catch(e => reject(e))
    })
  },
  download(url: string, name = "download.json") {
    getBlob(url)
    const link = document.createElement('a')
    link.href = url;
    link.download = name;
    link.click();
    link.remove();
  }
}

type TApi = typeof API;

export default () => {

  const api: any = Object.assign({}, API)

  api.init = (t: Partial<TApi>) => {
    Object.assign(api, t)
  }

  return api as TApi & { init: (api: Partial<TApi>) => void }
}